﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using TimedText.Styling;
using TimedTextLibrary;

namespace Microsoft.Office.PowerPoint.STAMP.Core
{
    /// <summary>
    /// Maps various classes
    /// </summary>
    public static class Mapper
    {
        /// <summary>
        /// Transforms list of ClosedCaption's to list of CaptionItem's. 
        /// </summary>
        /// <param name="captions">List of ClosedCaption's.</param>
        /// <returns>Returns list of CaptionItem's.</returns>
        public static IList<CaptionItem> ToCaptionItems(IEnumerable<ClosedCaption> captions)
        {
            if (captions == null) return null;

            return captions.Select(ToCaptionItem).ToList();
        }

        /// <summary>
        /// Transforms closed captions to captions class that contians list of caption items
        /// </summary>
        /// <param name="captionsList">List of closed captions.</param>
        /// <returns>Returns captions class.</returns>
        public static CaptionItemsList ToCaptions(ClosedCaptionsList captionsList)
        {
            if (captionsList == null) return null;

            return new CaptionItemsList { CaptionItems = new List<CaptionItem>(ToCaptionItems(captionsList.ClosedCaptions)) };
        }
        
        /// <summary>
        /// Transforms closed captions to captions class that contians list of caption items
        /// </summary>
        /// <param name="captionsList">List of closed captions.</param>
        /// <returns>Returns captions class.</returns>
        internal static CaptionItemsList ToCaptions(ClosedCaptionsSortableBindingList captionsList)
        {
            if (captionsList == null) return null;

            return new CaptionItemsList { CaptionItems = new List<CaptionItem>(ToCaptionItems(captionsList.ClosedCaptions)) };
        }
        /// <summary>
        /// Transforms closed captions to captions class that contians list of caption items
        /// </summary>
        /// <param name="captions">List of closed captions.</param>
        /// <returns>Returns captions class.</returns>
        internal static CaptionItemsList ToCaptions(IEnumerable<ClosedCaption> captions)
        {
            if (captions == null) return null;

            return new CaptionItemsList { CaptionItems = new List<CaptionItem>(ToCaptionItems(captions)) };
        }

        /// <summary>
        /// Transforms ClosedCaption to CaptionItem
        /// </summary>
        /// <param name="caption">ClosedCaption</param>
        /// <returns>Returns CaptionItem.</returns>
        internal static CaptionItem ToCaptionItem(ClosedCaption caption)
        {
            if (caption == null) return null;

            int appearPosition = Convert.ToInt32(caption.StartTime.TotalMilliseconds);
            int disappearPosition = Convert.ToInt32(caption.EndTime.TotalMilliseconds);

            string internalId = caption.IsNew ? Guid.NewGuid().ToString() : caption.Id;

            return new CaptionItem
                       {
                           InternalId = internalId,
                           Name =
                               Utils.CreateShapeName(caption.MediaShapeName, caption.CaptionText, appearPosition,
                                                     disappearPosition),
                           BookMarkEntryName =
                               Utils.CreateShapeName(caption.MediaShapeName, caption.CaptionText, appearPosition),
                           BookMarkExitName =
                               Utils.CreateShapeName(caption.MediaShapeName, caption.CaptionText, disappearPosition),
                           BookMarkEntryPosition = appearPosition,
                           BookMarkExitPosition = disappearPosition,
                           Text = caption.CaptionText,
                           IsNew = caption.IsNew,
                           IsDirty = caption.IsDirty,
                           IsDirtyCaptionText = caption.IsDirtyCaptionText,
                           IsDirtyStartTime = caption.IsDirtyStartTime,
                           IsDirtyEndTime = caption.IsDirtyEndTime,
                           CaptionStyle = caption.CaptionStyle ?? new CaptionStyle()
                       };
        }

        /// <summary>
        /// Transforms list of caption itmes to closed captions.
        /// </summary>
        /// <param name="captionItems">List of caption itmes.</param>
        /// <returns>Returns list of closed captions.</returns>
        internal static IList<ClosedCaption> ToClosedCaptions(IEnumerable<CaptionItem> captionItems)
        {
            if (captionItems == null) return null;

            return captionItems.Select(ToClosedCaption).ToList();
        }

        /// <summary>
        /// Transforms list of caption itmes to closed captions.
        /// </summary>
        /// <param name="captionItems">List of caption itmes.</param>
        /// <returns>Returns list of closed captions.</returns>
        internal static ClosedCaptionsList ToClosedCaptionsList(IEnumerable<CaptionItem> captionItems)
        {
            if (captionItems == null) return null;

            ClosedCaptionsList list = new ClosedCaptionsList
                                          {
                                              ClosedCaptions = captionItems.Select(ToClosedCaption).ToList()
                                          };

            return list;
        }

        /// <summary>
        /// Transforms caption item to closed caption.
        /// </summary>
        /// <param name="captionItem">Captions item.</param>
        /// <returns>Returns closed caption.</returns>
        internal static ClosedCaption ToClosedCaption(CaptionItem captionItem)
        {
            if (captionItem == null) return null;

            TimeSpan startTime = new TimeSpan(0, 0, 0, 0, captionItem.BookMarkEntryPosition);
            TimeSpan endTime = new TimeSpan(0, 0, 0, 0, captionItem.BookMarkExitPosition);

            return new ClosedCaption
                       {
                           CaptionText = captionItem.Text,
                           CaptionName = captionItem.Name,
                           Id = captionItem.InternalId,
                           StartTime = startTime,
                           EndTime = endTime,
                           CaptionStyle = captionItem.CaptionStyle
                       };
        }

        /// <summary>
        /// Tansforms list of strings to list of closed captions.
        /// </summary>
        /// <param name="stringses">List of strings.</param>
        /// <returns>Returns list of closed captions.</returns>
        internal static IList<ClosedCaption> ToClosedCaptions(IEnumerable<string[]> stringses)
        {
            if (stringses == null) return null;

            return stringses.Select(ToClosedCaption).ToList();
        }

        /// <summary>
        /// Transforms strings to closed caption.
        /// </summary>
        /// <param name="strings">Strings.</param>
        /// <returns>Returns closed caption.</returns>
        public static ClosedCaption ToClosedCaption(string[] strings)
        {
            if (strings == null || strings.Count() < 2) return null;

            TimeSpan startTime = TimeSpan.Parse(strings[0]);
            TimeSpan endTime = TimeSpan.Parse(strings[1]);

            CaptionStyle captionStyle = CaptionStyleParser.ToCaptionStyle(strings);

            return new ClosedCaption
                       {
                           CaptionText = strings[2],
                           StartTime = startTime,
                           EndTime = endTime,
                           IsNew = true,
                           CaptionStyle = captionStyle
                       };
        }

        /*  internal static Captions ToCaptions(IEnumerable<TimedTextElement> elements, string MediaShapeName)
          {
              if (elements == null) return null;

              return new Captions { CaptionItems = new List<CaptionItem>(ToCaptionItems(elements, MediaShapeName)) };
          }

          private static IEnumerable<CaptionItem> ToCaptionItems(IEnumerable<TimedTextElement> elements, string MediaShapeName)
          {
              if (elements == null) return null;

              return elements.Select(a => ToCaptionItem(a, MediaShapeName));
          }
          internal static CaptionItem ToCaptionItem(TimedTextElement element, string MediaShapeName)
          {
              if (element == null) return null;

              int appearPosition = Convert.ToInt32(element.Begin.TotalMilliseconds);
              int disappearPosition = Convert.ToInt32(element.End.TotalMilliseconds);

              string internalId = element.Id;

              string captionText = GetCaptionTextFromElement(element);
              //    CaptionStyle captionStyle = GetCaptionStyleFromElement(element);
              CaptionStyle captionStyle = CaptionStyleParser.MapStyle(element, null);

              return new CaptionItem
              {
                  InternalId = internalId,
                  Name = Utils.CreateShapeName(MediaShapeName, captionText, appearPosition,
                                            disappearPosition),
                  BookMarkEntryName =
                      Utils.CreateShapeName(MediaShapeName, captionText, appearPosition),
                  BookMarkExitName =
                      Utils.CreateShapeName(MediaShapeName, captionText, disappearPosition),
                  BookMarkEntryPosition = appearPosition,
                  BookMarkExitPosition = disappearPosition,
                  Text = captionText,
                  IsNew = true,
                  IsDirty = false,
                  IsDirtyCaptionText = false,
                  IsDirtyStartTime = false,
                  IsDirtyEndTime = false,
                  CaptionStyle = captionStyle
              };
          }
        /*  private static CaptionStyle GetCaptionStyleFromElement(TimedTextElement element)
          {
              return BuildElementStyle(element, null);
          }
          private static CaptionStyle BuildElementStyle(TimedTextElement element, CaptionStyle currentStyle)
          {
              CaptionStyle style = new CaptionStyle();
              style = CaptionStyleParser.MapStyle(element, currentStyle);

              foreach (TimedTextElement child in element.Children)
              {
                  BuildElementStyle(child, style);
              }
              return style;
          }

          private static string GetCaptionTextFromElement(TimedTextElement element)
          {
              string text = BuildElementText(element);

              return text;
          }
          private static string BuildElementText(TimedTextElement element)
          {
              StringBuilder sb = new StringBuilder();
              sb.Append(GetElementText(element));
              foreach (TimedTextElement child in element.Children)
              {
                  sb.Append(BuildElementText(child));
              }
              return sb.ToString();
          }

          private static string GetElementText(TimedTextElement timedTextElement)
          {
              string text = String.Empty;
              if (timedTextElement.CaptionElementType == TimedTextElementType.Text)
              {
                  text = timedTextElement.Content.ToString();
              }
              return text;
          }*/

        /// <summary>
        /// Transforms the TimedTextCaption collection to a collection of ClosedCaptions
        /// </summary>
        /// <param name="captions">The captions.</param>
        /// <returns></returns>
        internal static IList<ClosedCaption> ToClosedCaptions(IEnumerable<TimedTextCaption> captions)
        {
            if (captions == null) return null;

            return captions.Select(ToClosedCaption).ToList();
        }

        /// <summary>
        /// Transforms list of caption itmes to closed captions.
        /// </summary>
        /// <param name="timedTextCaptions">The timed text captions.</param>
        /// <returns>
        /// Returns list of closed captions.
        /// </returns>
        internal static ClosedCaptionsList ToClosedCaptionsList(IEnumerable<TimedTextCaption> timedTextCaptions)
        {
            if (timedTextCaptions == null) return null;

            ClosedCaptionsList list = new ClosedCaptionsList
            {
                ClosedCaptions = timedTextCaptions.Select(ToClosedCaption).ToList()
            };

            return list;
        }

        /// <summary>
        /// Transforms the TimedTextCaption to a ClosedCaption
        /// </summary>
        /// <param name="caption">Caption to map.</param>
        /// <returns>Returns closed caption.</returns>
        internal static ClosedCaption ToClosedCaption(TimedTextCaption caption)
        {
            if (caption == null) return null;

            CaptionStyle captionStyle = CaptionStyleParser.ToCaptionStyle(caption);

            return new ClosedCaption
            {
                CaptionText = caption.CaptionText,
                StartTime = caption.StartTime.Value,
                EndTime = caption.EndTime.Value,
                IsNew = true,
                CaptionStyle = captionStyle
            };
        }
    }

    internal static class CaptionStyleParser
    {
        private static readonly CaptionStyle DefaultStyle = new CaptionStyle();

        /*   internal static Color? GetBackGroundColor(TimedTextElement element)
           {
               Color? color = null;
               if (element.CaptionElementType == TimedTextElementType.Container || element.CaptionElementType == TimedTextElementType.Text)
               {
                   color = element.CurrentStyle.BackgroundColor;
               }
               foreach (TimedTextElement child in element.Children)
               {
                   color = GetBackGroundColor(child);
               }
               return color;
           }
           internal static Color? GetFontColor(TimedTextElement element)
           {
               Color? color = null;
               if (element.CaptionElementType == TimedTextElementType.Container || element.CaptionElementType == TimedTextElementType.Text)
               {
                   color = element.CurrentStyle.Color;
               }
               foreach (TimedTextElement child in element.Children)
               {
                   color = GetFontColor(child);
               }
               return color;
           }
           internal static FontFamily GetFontFamily(TimedTextElement element)
           {
               FontFamily temp = null;
               if (element.CaptionElementType == TimedTextElementType.Container || element.CaptionElementType == TimedTextElementType.Text)
               {
                   temp = element.CurrentStyle.FontFamily;
               }
               foreach (TimedTextElement child in element.Children)
               {
                   temp = GetFontFamily(child);
               }
               return temp;
           }

           internal static CaptionStyle MapStyle(TimedTextElement styleElement, CaptionStyle currentStyle)
           {
               var style = new CaptionStyle
               {
                   Id = styleElement.Id
               };

               var backgroundColor = GetBackGroundColor(styleElement);
               style.BackgroundColor = backgroundColor.HasValue ? backgroundColor.Value : DefaultStyle.BackgroundColor;

               var fontColor = GetFontColor(styleElement);
               style.FontColor = fontColor.HasValue ? fontColor.Value : DefaultStyle.FontColor;


               var fontFamily = GetFontFamily(styleElement);
               style.FontFamily = fontFamily ?? DefaultStyle.FontFamily;
               //todo fix that fontfamily relates back to - style.Font

               return style;
               //style.Font = new System.Drawing.Font(styleElement.CurrentStyle.FontFamily.Name, styleElement.CurrentStyle.FontSize.Value);

               //object oFontSize = styleElement.GetComputedStyle(TimedTextVocabulary.Attributes.Styling.FontSize.LocalName,
               //                                                 region);
               //var fontSize = oFontSize as string;
               //if (!fontSize.IsNullOrWhiteSpace())
               //{
               //    var parsedFontSize = new NumberPair(fontSize);
               //    style.FontSize = new Length
               //    {
               //        Unit = ConvertEnum<Unit, LengthUnit>(parsedFontSize.UnitMeasureHorizontal),
               //        Value = (float)parsedFontSize.First
               //    };
               //}

               //var fontStyle =
               //    styleElement.GetComputedStyle(TimedTextVocabulary.Attributes.Styling.FontStyle.LocalName, region) as
               //    FontStyleAttributeValue?;
               //style.FontStyle = fontStyle.HasValue &&
               //                  (fontStyle.Value == FontStyleAttributeValue.Italic ||
               //                   fontStyle.Value == FontStyleAttributeValue.Oblique ||
               //                   fontStyle.Value == FontStyleAttributeValue.ReverseOblique)
               //                      ? FontStyle.Italic
               //                      : DefaultStyle.FontStyle;

               //var fontWeight =
               //    styleElement.GetComputedStyle(TimedTextVocabulary.Attributes.Styling.FontWeight.LocalName, region) as
               //    FontWeightAttributeValue?;
               //style.FontWeight = fontWeight.HasValue && fontWeight.Value == FontWeightAttributeValue.Bold
               //                       ? FontStyle.Bold
               //                       : DefaultStyle.FontWeight;

               //var lineHeight =
               //    styleElement.GetComputedStyle(TimedTextVocabulary.Attributes.Styling.LineHeight.LocalName, region) as
               //    LineHeight;
               //style.LineHeight = lineHeight != null && !(lineHeight is NormalHeight)
               //                       ? new Length
               //                       {
               //                           Unit = ConvertEnum<Unit, LengthUnit>(lineHeight.UnitMeasureVertical),
               //                           Value = (float)lineHeight.Height
               //                       }
               //                       : null;

               //var opacity =
               //    styleElement.GetComputedStyle(TimedTextVocabulary.Attributes.Styling.Opacity.LocalName, region) as
               //    double?;
               //style.Opacity = opacity.HasValue
               //                    ? opacity.Value
               //                    : DefaultStyle.Opacity;

               //var origin =
               //    styleElement.GetComputedStyle(TimedTextVocabulary.Attributes.Styling.Origin.LocalName, region) as
               //    Origin;
               //style.Origin = origin != null
               //                   ? new CCAI.Office.PowerPoint.TimedText.Captions.Origin
               //                   {
               //                       Left = new Length
               //                       {
               //                           Unit =
               //                               ConvertEnum<Unit, LengthUnit>(origin.UnitMeasureHorizontal),
               //                           Value = (float)origin.X
               //                       },
               //                       Top = new Length
               //                       {
               //                           Unit = ConvertEnum<Unit, LengthUnit>(origin.UnitMeasureVertical),
               //                           Value = (float)origin.Y
               //                       }
               //                   }
               //                   : DefaultStyle.Origin;

               //var overflow =
               //    styleElement.GetComputedStyle(TimedTextVocabulary.Attributes.Styling.Overflow.LocalName, region) as
               //    string;
               //Overflow parsedOverflow;
               //style.Overflow = overflow.EnumTryParse(true, out parsedOverflow)
               //                     ? parsedOverflow
               //                     : DefaultStyle.Overflow;

               //var padding =
               //    styleElement.GetComputedStyle(TimedTextVocabulary.Attributes.Styling.Padding.LocalName, region) as
               //    PaddingThickness;
               //style.Padding = padding != null
               //                    ? new Padding
               //                    {
               //                        Left = new Length
               //                        {
               //                            Unit = ConvertEnum<Unit, LengthUnit>(padding.WidthStartUnit),
               //                            Value = (float)padding.WidthStart
               //                        },
               //                        Right = new Length
               //                        {
               //                            Unit = ConvertEnum<Unit, LengthUnit>(padding.WidthEndUnit),
               //                            Value = (float)padding.WidthEnd
               //                        },
               //                        Top = new Length
               //                        {
               //                            Unit = ConvertEnum<Unit, LengthUnit>(padding.WidthBeforeUnit),
               //                            Value = (float)padding.WidthBefore
               //                        },
               //                        Bottom = new Length
               //                        {
               //                            Unit = ConvertEnum<Unit, LengthUnit>(padding.WidthAfterUnit),
               //                            Value = (float)padding.WidthAfter
               //                        }
               //                    }
               //                    : DefaultStyle.Padding;

               ////var textAlign =
               ////    styleElement.GetComputedStyle(TimedTextVocabulary.Attributes.Styling.TextAlign.LocalName, region) as
               ////    string;
               ////TextAlignment parsedTextAlign;
               ////if (textAlign == "start") textAlign = "left";
               ////else if (textAlign == "end") textAlign = "right";
               ////style.TextAlign = textAlign.EnumTryParse(true, out parsedTextAlign)
               ////                      ? parsedTextAlign
               ////                      : DefaultStyle.TextAlign;

               //var displayAlign =
               //    styleElement.GetComputedStyle(TimedTextVocabulary.Attributes.Styling.DisplayAlign.LocalName, region) as
               //    string;
               //DisplayAlign parsedDisplayAlign;
               //style.DisplayAlign = displayAlign.EnumTryParse(true, out parsedDisplayAlign)
               //                         ? parsedDisplayAlign
               //                         : DefaultStyle.DisplayAlign;

               ////var visibility =
               ////    styleElement.GetComputedStyle(TimedTextVocabulary.Attributes.Styling.Visibility.LocalName, region) as
               ////    string;
               ////style.Visibility = !visibility.IsNullOrWhiteSpace() &&
               ////                   visibility.Equals("hidden", StringComparison.CurrentCultureIgnoreCase)
               ////                       ? Visibility.Collapsed
               ////                       : DefaultStyle.Visibility;

               ////var display =
               ////    styleElement.GetComputedStyle(TimedTextVocabulary.Attributes.Styling.Display.LocalName, region) as
               ////    string;
               ////style.Display = !display.IsNullOrWhiteSpace() &&
               ////                display.Equals("none", StringComparison.CurrentCultureIgnoreCase)
               ////                    ? Visibility.Collapsed
               ////                    : DefaultStyle.Display;

               ////var wrapOption =
               ////    styleElement.GetComputedStyle(TimedTextVocabulary.Attributes.Styling.WrapOption.LocalName, region) as
               ////    string;
               ////TextWrapping parsedWrapOption;
               ////style.WrapOption = wrapOption.EnumTryParse(true, out parsedWrapOption)
               ////                       ? parsedWrapOption
               ////                       : DefaultStyle.WrapOption;

               //var showBackground =
               //    styleElement.GetComputedStyle(TimedTextVocabulary.Attributes.Styling.ShowBackground.LocalName, region)
               //    as string;
               //ShowBackground parsedShowBackground;
               //style.ShowBackground = showBackground.EnumTryParse(true, out parsedShowBackground)
               //                           ? parsedShowBackground
               //                           : DefaultStyle.ShowBackground;

               //object zindex = styleElement.GetComputedStyle("zIndex", null);
               //try
               //{
               //    if (zindex is string == false)
               //    {
               //        var tmp = (double)zindex;
               //        style.ZIndex = (int)tmp;
               //    }
               //    else
               //    {
               //        style.ZIndex = 0;
               //    }
               //}
               //catch
               //{
               //    style.ZIndex = 0;
               //}

           
           }*/

        internal static CaptionStyle ToCaptionStyle(string[] strings)
        {
            var style = new CaptionStyle();

            if (strings.Count() > 8)
            {

                var fontWeight = Enum.Parse(typeof(FontWeightAttributeValue), strings[3]) as FontWeightAttributeValue?;
                style.FontWeight = fontWeight.HasValue && fontWeight.Value == FontWeightAttributeValue.bold
                                       ? FontStyle.Bold
                                       : DefaultStyle.FontWeight;

                var fontStyle = Enum.Parse(typeof(FontStyleAttributeValue), strings[4]) as FontStyleAttributeValue?;
                style.FontStyle = fontStyle.HasValue &&
                                  (fontStyle.Value == FontStyleAttributeValue.italic ||
                                   fontStyle.Value == FontStyleAttributeValue.oblique)
                                      ? FontStyle.Italic
                                      : DefaultStyle.FontStyle;

                var textDecoration =
                    Enum.Parse(typeof(TextDecorationAttributeValue), strings[5]) as TextDecorationAttributeValue?;
                style.TextDecorationStyleProperty = textDecoration.HasValue
                                                        ? textDecoration.Value
                                                        : DefaultStyle.TextDecorationStyleProperty;

                var textAlign = Enum.Parse(typeof(TextAlignAttibuteValue), strings[6]) as TextAlignAttibuteValue?;
                style.TextAlignStyleProperty = textAlign.HasValue
                                                   ? textAlign.Value
                                                   : DefaultStyle.TextAlignStyleProperty;

                var backgroundColor = Color.FromName(strings[7]) as Color?;
                style.BackgroundColor = backgroundColor.HasValue
                                            ? backgroundColor.Value
                                            : DefaultStyle.BackgroundColor;

                var fontColor = Color.FromName(strings[8]) as Color?;
                style.FontColor = fontColor.HasValue
                                      ? fontColor.Value
                                      : DefaultStyle.FontColor;
            }
            return style;
        }

        internal static CaptionStyle ToCaptionStyle(TimedTextCaption caption)
        {
            var style = new CaptionStyle();

            style.FontColor = caption.FontColor.HasValue ? caption.FontColor.Value : DefaultStyle.FontColor;

            style.BackgroundColor = caption.BackgroundColor.HasValue ? caption.BackgroundColor.Value : DefaultStyle.BackgroundColor;

            if (style.BackgroundColor == Color.Transparent)
            {
                style.BackgroundColor = DefaultStyle.BackgroundColor;
                if (style.FontColor == Color.Black)
                {
                    style.FontColor = DefaultStyle.FontColor;
                }
            }

            if (caption.FontSize != null)
            {
                style.FontSize = new Length
                   {
                       Unit = ConvertEnum<Unit, LengthUnit>(caption.FontSize.UnitMeasureHorizontal),
                       Value = (float)caption.FontSize.First
                   };
            }

            style.FontWeight = caption.FontWeight.HasValue ? caption.FontWeight.Value : DefaultStyle.FontWeight;
            style.FontStyle = caption.FontStyle.HasValue ? caption.FontStyle.Value : DefaultStyle.FontStyle;

            TextDecorationAttributeValue parsedDecorationAttributeValue;
            style.TextDecorationStyleProperty = caption.TextDecoration.EnumTryParse(true,
                                                                                    out parsedDecorationAttributeValue)
                                                    ? parsedDecorationAttributeValue
                                                    : DefaultStyle.TextDecorationStyleProperty;

            TextAlignAttibuteValue parsedTextAlign;
            style.TextAlignStyleProperty = caption.TextAlign.EnumTryParse(true, out parsedTextAlign)
                                          ? parsedTextAlign
                                          : DefaultStyle.TextAlignStyleProperty;


            return style;
        }

        private static TResult ConvertEnum<TSource, TResult>(TSource source)
            where TSource : struct
            where TResult : struct
        {
            TResult result;
            return source.ToString().EnumTryParse(true, out result)
                       ? result
                       : default(TResult);
        }
    }
}